﻿// Open Source: MIT License (http://www.opensource.org/licenses/mit-license.php)
// Project: YoProgramo.NET (http://code.google.com/p/yoprogramo/)
// Autor: Fabian Figueredo (info@fabianfigueredo.com.ar)
// Blog: blog.yoprogramo.net

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using System.Web.UI;
using NHibernate.Validator.Engine;
using YoProgramo.Domain.BusinessLogic.Extensions;
using YoProgramo.Domain.BusinessLogic.Service;
using YoProgramo.Domain.Core.Exceptions;
using YoProgramo.Domain.Core.Interfaces;
using YoProgramo.Domain.Core.MemberShip;
using YoProgramo.Infrastructure.Tools.Configuration;

namespace YoProgramo.Web.Controllers {

    [HandleError]
    public class AccountController : Controller {

        public AccountController(IFormsAuthentication formsAuth, IMembershipService service, IUserService userservice, IMailService mailservice) {
            FormsAuth = formsAuth ?? new FormsAuthenticationService();
            MembershipService = service;
            UserService = userservice;
            MailService = mailservice;
        }

        public IFormsAuthentication FormsAuth {
            get;
            private set;
        }

        public IMailService MailService {
            get;
            private set;
        }

        public IMembershipService MembershipService {
            get;
            private set;
        }

        public IUserService UserService {
            get;
            private set;
        }

        public ActionResult LogOn() {

            return View();
        }

        [AcceptVerbs(HttpVerbs.Post)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
            Justification = "Needs to take same parameter type as Controller.Redirect()")]
        public ActionResult LogOn(string userName, string password, bool rememberMe, string returnUrl) {

            if (!ValidateLogOn(userName, password)) {
                return View();
            }

            FormsAuth.SignIn(userName, rememberMe);

            IUser user = UserService.GetByUserName(userName);
            user.UpdateLastActivityDate();

            if (!String.IsNullOrEmpty(returnUrl)) {
                return Redirect(returnUrl);
            } else {
                return RedirectToAction("Index", "Home");
            }
        }

        public ActionResult LogOff() {

            FormsAuth.SignOut();

            return RedirectToAction("Index", "Home");
        }

        public ActionResult Register() {
            if (YoProgramoConfigurationManager.IsRegistrationLocked())
                throw new YPException("La registración se encuentra cerrada");

            ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

            return View();
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Register(string userName, string email, string password, string confirmPassword) {

            if (YoProgramoConfigurationManager.IsRegistrationLocked())
                throw new YPException("La registración se encuentra cerrada");
            

            ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

            if (ValidateRegistration(userName, email, password, confirmPassword)) {
                // Attempt to register the user

                try{
                    InvalidValue[] createStatus = CreateUser(userName, email, password);
                        if (createStatus.Count() < 1) {
                            FormsAuth.SignIn(userName, false /* createPersistentCookie */);
                            MailService.SendMail(string.Empty ,userName,MailType.Welcome, ExtraTools.GetMailTemplateFolder());
                            return RedirectToAction("Index", "Home");
                        } else {
                            foreach (InvalidValue error in createStatus)
                            {
                                ModelState.AddModelError(error.PropertyName, error.Message);
                            }
                        }
                    } catch (YPException ex)
                    {
                        ModelState.AddModelError(ex.Source, ex.Message);
                    }

            }
            // If we got this far, something failed, redisplay form
            return View();
        }

        private InvalidValue[] CreateUser(string userName, string email, string password)
        {
            if (UserService.GetByUserName(userName) != null)
                throw new YPException("UserName ya se encuentra en uso");

            if (UserService.GetByEmail(email) != null)
                throw new YPException("Email ya se encuentra en uso");

            try {

                return MembershipService.CreateUser(userName, password, email);
            }
            catch(Exception ex)
            {
                throw new YPException("Error al crear Usuario", ex);
            }            
        }

        [Authorize]
        public ActionResult ChangePassword() {

            ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

            return View();
        }

        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
            Justification = "Exceptions result in password not being changed.")]
        public ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword) {

            ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

            if (!ValidateChangePassword(currentPassword, newPassword, confirmPassword)) {
                return View();
            }

            try {
                if (MembershipService.ChangePassword(User.Identity.Name, currentPassword, newPassword)) {
                    return RedirectToAction("ChangePasswordSuccess");
                } else {
                    ModelState.AddModelError("_FORM", "The current password is incorrect or the new password is invalid.");
                    return View();
                }
            } catch {
                ModelState.AddModelError("_FORM", "The current password is incorrect or the new password is invalid.");
                return View();
            }
        }

        public ActionResult ChangePasswordSuccess() {

            return View();
        }

        protected override void OnActionExecuting(ActionExecutingContext filterContext) {
            if (filterContext.HttpContext.User.Identity is WindowsIdentity) {
                throw new InvalidOperationException("Windows authentication is not supported.");
            }
        }

        #region Validation Methods

        private bool ValidateChangePassword(string currentPassword, string newPassword, string confirmPassword) {
            if (String.IsNullOrEmpty(currentPassword)) {
                ModelState.AddModelError("currentPassword", "Debes introducir tu contraseña actual.");
            }
            if (newPassword == null || newPassword.Length < MembershipService.MinPasswordLength) {
                ModelState.AddModelError("newPassword",
                    String.Format(CultureInfo.CurrentCulture,
                         "La nueva contraseña debe tener al menos {0} caracteres.",
                         MembershipService.MinPasswordLength));
            }

            if (!String.Equals(newPassword, confirmPassword, StringComparison.Ordinal)) {
                ModelState.AddModelError("_FORM", "Las contraseñas no son iguales.");
            }

            return ModelState.IsValid;
        }

        private bool ValidateLogOn(string userName, string password) {
            if (String.IsNullOrEmpty(userName)) {
                ModelState.AddModelError("username", "Debes introducir el Usuario.");
            }
            if (String.IsNullOrEmpty(password)) {
                ModelState.AddModelError("password", "Debes introducir la Contraseña.");
            }
            if (!MembershipService.ValidateUser(userName, ExtraTools.EncodeMD5(password))) {
                ModelState.AddModelError("_FORM", "Usuario o Contraseña son incorrectos.");
            }

            return ModelState.IsValid;
        }

        private bool ValidateRegistration(string userName, string email, string password, string confirmPassword) {
            if (String.IsNullOrEmpty(userName)) {
                ModelState.AddModelError("username", "UserName no puede ser Nulo.");
            }
            if (String.IsNullOrEmpty(email)) {
                ModelState.AddModelError("email", "EMail no puede ser Nulo.");
            }
            if (password == null || password.Length < MembershipService.MinPasswordLength) {
                ModelState.AddModelError("password",
                    String.Format(CultureInfo.CurrentCulture,
                         "Contraseña debe tener {0} o más caracteres.",
                         MembershipService.MinPasswordLength));
            }
            if (!String.Equals(password, confirmPassword, StringComparison.Ordinal)) {
                ModelState.AddModelError("_FORM", "Las contraseñas no son iguales.");
            }
            return ModelState.IsValid;
        }        

        #endregion
    }

}
